/*
 *  Layout of output file of pack_image():
 *      * XBoot-0 (file: argv[ARGC_PACK_IMAGE_XBOOT0_FILE], size: FILE_SIZE_IMAGE_XBOOT0):
 *          * Not programmed to NAND/eMMC
 *          * Loaded by IBoot
 *      * UBoot-0 (file: argv[ARGC_PACK_IMAGE_UBOOT0_FILE], size: FILE_SIZE_IMAGE_UBOOT0):
 *          * Not programmed to NAND/eMMC
 *          * Loaded by XBoot
 *          * Used for ISP.
 *      * Information about this file (struct file_header_s).
 *          * Why it's not at beginning of this file?
 *            IBoot just loads first few KB of output file and run it.
 *            It doesn't know about this structure.
 *          * Not programmed to NAND/eMMC.
 *      * File for XBoot-1
 *      * File for UBoot-1 (factory default)
 *      * File for UBoot-2 (latest update)
 *      * File for Partition-0
 *      * File for Partition-1
 *      * ...
 *      * main ISP script, generated by gen_script_main()
 *          * Not programmed to NAND/eMMC
 *
 *  Layout of eMMC:
 *      * GUID Partition Table, GPT.
 *      * XBoot-1
 *      * UBoot-1 (factory default)
 *      * UBoot-2 (latest update)
 *      * ...
 *  Layout of NAND:
 *      * Header, mostly NAND parameters, programmed by UBoot command "nand write.bhdr auto ..."
 *      * XBoot-1, programmed by "nand write.bblk ...", BCH_1K60 is in payload to protect contents.
 *      * UBoot-1 (factory default), programmed by "nand write.bblk ...", BCH_1K60 is in payload to protect contents.
 *      * UBoot-2 (latest update), programmed by "nand write.bblk ...", BCH_1K60 is in payload to protect contents.
 *      * Partition information, programmed by "nand write ...". The contents is created in U-Boot at run-time.
 *      * Partition-0, programmed by "nand write ...".
 *      * Partition-1, programmed by "nand write ...".
 *      * ...
 */

#define _BSD_SOURCE
#ifndef __MINGW32__
#include <endian.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
#include <stddef.h>
#include <time.h>
#include <libgen.h>

//#define DEBUG_ISP
#if defined(DEBUG_ISP)
#define MESSAGE_OUT  ""
#else
#define MESSAGE_OUT_NONE
#ifndef __MINGW32__
#define MESSAGE_OUT  " 1> /dev/null 2>&1 "
#else
#define MESSAGE_OUT  " 2> nul > nul"
#endif
#endif

#define SUPPORT_MAIN_STORAGE_IS_EMMC
#define XBOOT1_IN_EMMC_BOOTPART

#define REDUCE_MESSAGE
#define PROGRAM_HEADER_LAST  // Move "programming header" to the last programming step
#define ADDRESS_FATLOAD     (16 << 20)                      // default area for U-Boot's fatload

#define ALIGN_TO_1K(X)      ( (X + ((1<<10)-1)) & (0xFFFFFFFF - ((1<<10)-1)) )
#define ALIGN_TO_1M(X)      ( (X + ((1<<20)-1)) & (0xFFFFFFFF - ((1<<20)-1)) )





// For command "isp pack_image ..."
#define ARGC_PACK_IMAGE_MAINCMD                      (0)    // isp
#define ARGC_PACK_IMAGE_SUBCMD                       (1)    // pack_image
#define ARGC_PACK_IMAGE_OUTPUT                       (2)    // Should be ISPBOOOT.BIN, IBoot expects it.
#define ARGC_PACK_IMAGE_XBOOT0_FILE                  (3)    // XBOOT used in ISP
#define ARGC_PACK_IMAGE_UBOOT0_FILE                  (4)    // UBOOT used in ISP
#define ARGC_PACK_IMAGE_XBOOT1_FILE                  (5)    // XBOOT programmed in NAND/eMMC
#define ARGC_PACK_IMAGE_XBOOT1_PARTITION_SIZE        (6)
#define ARGC_PACK_IMAGE_UBOOT1_FILE                  (7)    // UBOOT-1 (factory default) programmed in NAND/eMMC
#define ARGC_PACK_IMAGE_UBOOT1_PARTITION_SIZE        (8)
#define ARGC_PACK_IMAGE_UBOOT2_FILE                  (9)    // UBOOT-2 (latest update) programmed in NAND/eMMC
#define ARGC_PACK_IMAGE_UBOOT2_PARTITION_SIZE       (10)
#define ARGC_PACK_IMAGE_BINARY_PARTITION1_FILE      (11)
#define ARGC_PACK_IMAGE_BINARY_PARTITION1_SIZE      (12)
#define ARGC_PACK_IMAGE_BINARY_PARTITION2_FILE      (13)
#define ARGC_PACK_IMAGE_BINARY_PARTITION2_SIZE      (14)

// For command "isp extract4update ..."
#define ARGC_EXTRACT4UPDATE_MAINCMD                  (0)    // isp
#define ARGC_EXTRACT4UPDATE_SUBCMD                   (1)    // extract4update
#define ARGC_EXTRACT4UPDATE_INPUT                    (2)    // Should be ISPBOOOT.BIN
#define ARGC_EXTRACT4UPDATE_OUTPUT                   (3)    // ISP_UPDT.BIN, expected by U-Boot's partial update command
#define ARGC_EXTRACT4UPDATE_PARTITION0               (4)    // first partition to be updated
#define ARGC_EXTRACT4UPDATE_PARTITION1               (5)    // second partition to be updated

// For command "isp extract4boot2linux ..."
#define ARGC_EXTRACT4BOOT2LINUX_MAINCMD              (0)    // isp
#define ARGC_EXTRACT4BOOT2LINUX_SUMCMD               (1)    // extract4boot2linux
#define ARGC_EXTRACT4BOOT2LINUX_INPUT                (2)    // Should be ISPBOOOT.BIN
#define ARGC_EXTRACT4BOOT2LINUX_OUTPUT               (3)    // Should be ISPBOOOT.BIN, but in different folder



#define FILE_SIZE_IMAGE_XBOOT0                      (64 << 10)
#define FILE_SIZE_IMAGE_UBOOT0                      ((1 << 20) - FILE_SIZE_IMAGE_XBOOT0)

#define NAND_READ_BY_PARTITION_NAME                         // if not defined, it's by NAND address
// #define PARTITION_SIZE_BAD_BLOCK_DOES_NOT_COUNT

// NAND 1st partition address will affect env and env_redund partition address.
// If it is changed, env address (CONFIG_ENV_OFFSET) and env_redund (CONFIG_ENV_OFFSET_REDUND)
// in pentagram_common.h also need to be changed.
#define ADDRESS_NAND_1ST_PARTITION                  0x400000

#define MAX_MEM_SIZE_FOR_ISP                        (2 << 20)       // Must be N*(block size), where N=1, 2, ...
#define GPT_HEADER_SIZE                             (17 << 10)      // GUID Partition header size: (512-byte MBR) + (512-byte header) + (128 bytes * 128 partitions)

typedef unsigned char u08;
typedef uint32_t      u32;
const u08 file_header_signature[] = "Pentagram_ISP_image";

#define SIZE_FILE_NAME      (32)
struct partition_info_s {
	u08 file_name[SIZE_FILE_NAME];  // file name of source (basename only)
	u08 md5sum[36];
	u32 file_offset;                // offset in output file
	u32 file_size;                  // file size of the partition
	u32 partition_start_addr;       // partition's start address in NAND, there will be an offset added in U-Boot script ($isp_nand_addr_1st_part), less than 4GB is expected.
	// for files before argv[ARGC_PACK_IMAGE_BINARY_PARTITION1_FILE], it's zero
	// this value is none zero for files, which start from argv[ARGC_PACK_IMAGE_BINARY_PARTITION2_FILE]
	// Reason: Start address of followings are calculated at run-time:
	//     Header, XBoot-1, UBoot-1, ..., and Partition-0.
	u32 partition_size;             // reserved size for this partition, less than 4GB is expected.
	u32 flags;
	u32 emmc_partition_start;       // Unit: block
	u32 reserved[9];                // let size of this structure == SIZE_PARTITION_INFO_S
} __attribute__((packed));
#define SIZE_PARTITION_INFO_S   (128)

#define FLAGS_BCH1K60                   (1 << 0)    // partitions r/w by "nand read.bblk" and "nand write.bblk".
#define FLAGS_BCH1K60_LAST_ONE          (1 << 1)
#define FLAGS_NOT_ALLOWED_TO_UPDATE     (1 << 2)

#define IDX_PARTITION_XBOOT1            (0)
#define IDX_PARTITION_UBOOT1            (1)
#define IDX_PARTITION_UBOOT2            (2)

#define SIZE_INIT_SCRIPT    (2048)
#define NUM_OF_PARTITION    (111)
struct file_header_s {
	u08 signature[32];
	u08 init_script[SIZE_INIT_SCRIPT];
	u32 flags;
	u08 reserved[SIZE_PARTITION_INFO_S - 36];

	struct partition_info_s partition_info[NUM_OF_PARTITION];
} __attribute__((packed));          // sizeof(this structure) == 16384

#define FLAGS_MTD_ONLY                  (1 << 0)

#define SIZE_FULL_FILE_NAME             (256)
struct isp_info_s {
	struct file_header_s file_header;

	u08 full_file_name[NUM_OF_PARTITION][SIZE_FULL_FILE_NAME];
	u08 full_file_name_xboot0[SIZE_FULL_FILE_NAME];
	u08 full_file_name_uboot0[SIZE_FULL_FILE_NAME];
	u08 file_name_pack_image[SIZE_FULL_FILE_NAME];
	// u08 base_file_name_pack_image[SIZE_FILE_NAME];
	int nand_block_size;
	int idx_partition;
	u32 flags;
	FILE *fd;
	char file_disk_image[32];
	int idx_gpt_header_primary;
	u08 *key_ptr;
};
#define FLAGS_STAGE_WRITE               (1 << 0)
#define FLAGS_STAGE_VERIFY              (1 << 1)
#define FLAGS_GPT_CREATED               (1 << 2)
#define FLAGS_UPDATE_MODE               (1 << 3)
#define FLAGS_ENCRYPTION_ENABLE         (1 << 4)

struct isp_info_s isp_info;

#define BYTE2BLOCK(x)                   ((x + 511) >> 9)    /* 512 bytes/sector */

void dump_isp_info(void)
{
	int i;

	printf("\n%s\n\n", __func__);
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (isp_info.file_header.partition_info[i].partition_size ||
		    isp_info.file_header.partition_info[i].file_size ||
		    isp_info.file_header.partition_info[i].file_name[0]) {
			printf("%3d: 0x%08x/0x%08x, 0x%08x, %-36s, 0x%08x, %s, %s\n", i,
			       isp_info.file_header.partition_info[i].file_size,
			       isp_info.file_header.partition_info[i].partition_size,
			       isp_info.file_header.partition_info[i].partition_start_addr,
			       isp_info.file_header.partition_info[i].file_name,
			       isp_info.file_header.partition_info[i].file_offset,
			       isp_info.file_header.partition_info[i].md5sum,
			       isp_info.full_file_name[i]);
		}
	}
	// printf("isp_info.file_header.flags: 0x%08x\n", isp_info.file_header.flags);

	printf("\n");
}

int get_partition_info_idx_by_file_name(char *file_name_ptr)
{
	int i;

	// Note:
	//      It assumes that all the file names are different. There is no sanity check here.
	// For example,
	//      If files for 3 U-Boot files are the same (argv[ARGC_PACK_IMAGE_XBOOT0_FILE], argv[ARGC_PACK_IMAGE_XBOOT1_FILE], argv[ARGC_PACK_IMAGE_XBOOT2_FILE]),
	//      please copy it to 3 files with different file names.
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (strncmp( basename(isp_info.file_header.partition_info[i].file_name), basename( file_name_ptr), SIZE_FILE_NAME) == 0) {
			return i;
			// break;
		}
	}

	printf("\nCan't find file %s\n\n", file_name_ptr);
	return -1;
}

int md5sum(char *file_name, u32 offset, u32 size, char *md5sum_ptr)
{
	char tmp_file[32];
	char cmd[1024];
	FILE *fd;

	if ((offset == 0) && (size == 0)) { /* md5sum of the whole file */
		sprintf(cmd, "md5sum %s", file_name);
		fd = popen(cmd, "r");
		if (fd == NULL) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}
		if (fscanf(fd, "%s", md5sum_ptr) != 1) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}
		pclose(fd);

		return 0;
	}

	if ((offset & 0x03FF) != 0) { // check if it's 1 KB aligned
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}

	sprintf(tmp_file, "tmp_md5sum%08x", (u32)(getpid()));
	if ((size & 0x03FF) != 0)
		sprintf(cmd, "dd if=%s of=%s bs=1 skip=%u count=%u %s", file_name, tmp_file, offset, size, MESSAGE_OUT);
	else
		sprintf(cmd, "dd if=%s of=%s bs=1024 skip=%u count=%u %s", file_name, tmp_file, (offset >> 10), (size >> 10), MESSAGE_OUT);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "md5sum %s", tmp_file);
	fd = popen(cmd, "r");
	if (fd == NULL) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
	if (fscanf(fd, "%s", md5sum_ptr) != 1) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
	pclose(fd);

	sprintf(cmd, "rm %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);

	return 0;
}

int compile_string_for_part_sizes(char *partition_info_ptr, int max_size)
{
	int i;
	int string_size;
	int is_1MB_aligned;

	string_size = 0;
	memset(partition_info_ptr, 0, max_size);
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if ((isp_info.file_header.partition_info[i].flags & FLAGS_NOT_ALLOWED_TO_UPDATE) ||
		    (isp_info.file_header.partition_info[i].partition_size == 0))
			continue;

		is_1MB_aligned = ((isp_info.file_header.partition_info[i].partition_size & ((1 << 20) - 1)) == 0) ? 1 : 0;
		string_size += snprintf((partition_info_ptr + string_size), (max_size - string_size),
					(is_1MB_aligned ? "%s_%uM" : "%s_%uK"),
					basename( isp_info.file_header.partition_info[i].file_name),
					(is_1MB_aligned ? (isp_info.file_header.partition_info[i].partition_size >> 20) :
					 (isp_info.file_header.partition_info[i].partition_size >> 10)));
	}

	return 0;
}

#if 0
u32 cal_block_size_required(void)
{
	int i;
	u32 bit_mask;
	u32 block_size_required;

	bit_mask = 0;
	for (i = 0; i < NUM_OF_PARTITION; i++)
		bit_mask |= isp_info.file_header.partition_info[i].partition_size;

	for (i = 0; i < 32; i++) {
		if (bit_mask & 0x00000001)
			break;
		bit_mask = bit_mask >> 1;
	}
	block_size_required = 1 << i;

	// Check if sizes of partitions are (N * block_size_required)
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (isp_info.file_header.partition_info[i].partition_size & (block_size_required - 1)) {
			printf("Error: partition size of %s isn't NAND block-size aligned.\n", basename( isp_info.file_header.partition_info[i].file_name));
			exit(-1);
		}
	}

	return block_size_required;
}

u32 cal_total_nand_size_required(void)
{
	int i;
	u32 total_nand_size_required;

	total_nand_size_required = 0;
	for (i = 0; i < NUM_OF_PARTITION; i++)
		total_nand_size_required += isp_info.file_header.partition_info[i].partition_size;
	total_nand_size_required += total_nand_size_required >> 5;   // Add 3.125% (1/32) for bad blocks, and nand_header, partitions using 1K60-BCH encoding.
	return total_nand_size_required;
}
#endif

#define IDX_NAND        (0)
#define IDX_EMMC        (1)
#define NUM_STORAGE     (2)

int gen_script_main(char *file_name_isp_script, int nand_or_emmc)
{
	FILE *fd;
	char tmp_file[32], cmd[1024];
	int i, j, idx_nand_write_bblk, idx;
	u32 file_size,file_offset, size, size_programmed, size_verified;
	u32 flag_first;
	char uboot_var_part_sizes[2048];
	int is_1MB_aligned;

	sprintf(tmp_file, "tmp%08x", (u32)(getpid()));

	fd = fopen(tmp_file, "wb");
	if (fd == NULL) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		return -1;
	}

	if (nand_or_emmc == IDX_NAND) {
		fprintf(fd, "nand info\n");             // After executing this command, $nand_erasesize and $nand_writesize are available.
		// fprintf(fd, "setenv isp_block_size 0x${nand_erasesize}\n");
		// fprintf(fd, "nand scrub.chip\n");
		fprintf(fd, "nand erase.chip\n");
		fprintf(fd, "nand bad\n");
		fprintf(fd, "\n");

#if 0
		fprintf(fd, "ispsp sanity_check 0x%x 0x%x\n", cal_block_size_required(), cal_total_nand_size_required());
		fprintf(fd, "if test \"$?\" = 0 ; then\n");
		fprintf(fd, "    echo ispsp sanity_check: OK.\n");
		fprintf(fd, "else\n");
		fprintf(fd, "    echo ispsp sanity_check: Error!\n");
		fprintf(fd, "    exit -1\n");
		fprintf(fd, "fi\n\n");
#endif

		fprintf(fd, "mtdparts default && mtdparts delall\n\n");

		fprintf(fd, "echo\n");

#ifdef PROGRAM_HEADER_LAST
		// Only its partition is assigned now.
		// We write the header later because some information is not available (eg. env offset) at this point.
		fprintf(fd, "setenv isp_addr_next ${nand_erasesize}\n");
#else
		fprintf(fd, "echo programming header (mostly NAND parameters) ...\n");
		// "nand erase.chip" already,
		// fprintf(fd, "nand erase 0x00000000 0x0100\n");
		idx = get_partition_info_idx_by_file_name(isp_info.file_header.partition_info[IDX_PARTITION_XBOOT1].file_name);
		if (idx < 0) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}
		fprintf(fd, "nand write.bhdr auto 0 0x%x\n", isp_info.file_header.partition_info[idx].file_size);
		// UBoot's $isp_addr_next is available after "nand write.bhdr"
#endif

		// $isp_addr_next is the start address of next block after "nand write.bhdr"

		fprintf(fd, "setenv isp_mtdpart_size 0x${isp_addr_next}\n");

#if !defined(REDUCE_MESSAGE)
		snprintf(cmd, sizeof(cmd), "mtdparts add nand0 ${isp_mtdpart_size}@0x00000000 nand_header && mtdparts && printenv mtdparts");
#else
		snprintf(cmd, sizeof(cmd), "mtdparts add nand0 ${isp_mtdpart_size}@0x00000000 nand_header && printenv mtdparts");
#endif
		fprintf(fd, "echo %s\n", cmd);
		fprintf(fd, "%s\n\n", cmd);
	} else if (nand_or_emmc == IDX_EMMC) {
		fprintf(fd, "echo Initialize eMMC ...\n");
		fprintf(fd, "mmc dev 0 && mmc rescan\n\n");
#ifndef XBOOT1_IN_EMMC_BOOTPART
		fprintf(fd, "echo Destroy data in eMMC boot area.\n");
		fprintf(fd, "mw.b $isp_ram_addr 0xFF 0x0400\n");
		fprintf(fd, "mmc partconf 0 0 7 1\n");
		fprintf(fd, "mmc write $isp_ram_addr 0x0000 0x0002\n");
		fprintf(fd, "mmc partconf 0 0 0 0\n");
#endif
		fprintf(fd, "mw.b $isp_ram_addr 0xFF 0x4400 && mmc write $isp_ram_addr 0x00 0x22\n");  // fill GPT with 0xFF
		fprintf(fd, "mmc dev 0 && mmc rescan\n\n");

		fprintf(fd, "uuid uuid_gpt_disk\n");
		for (i = 0; i < NUM_OF_PARTITION; i++) {
			if (isp_info.file_header.partition_info[i].file_name[0] == 0x00)
				break;

			fprintf(fd, "uuid uuid_gpt_%s\n", basename( isp_info.file_header.partition_info[i].file_name));
		}
		fprintf(fd, "uuid uuid_gpt_userdata\n");
		fprintf(fd, "\n");

		fprintf(fd, "setenv partitions \"uuid_disk=${uuid_gpt_disk};");
		for (i = 0; i < NUM_OF_PARTITION; i++) {
			if (isp_info.file_header.partition_info[i].file_name[0] == 0x00)
				break;
#ifdef XBOOT1_IN_EMMC_BOOTPART
			if (i == IDX_PARTITION_XBOOT1) {
				continue;
			}
#endif
			is_1MB_aligned = ((isp_info.file_header.partition_info[i].partition_size & ((1 << 20) - 1)) == 0) ? 1 : 0;

			fprintf(fd, "name=%s,", basename( isp_info.file_header.partition_info[i].file_name));
			fprintf(fd, "size=%u%s,", (is_1MB_aligned ? (isp_info.file_header.partition_info[i].partition_size >> 20) :
						   (isp_info.file_header.partition_info[i].partition_size >> 10)),
				(is_1MB_aligned ? "MiB" : "KiB"));
			fprintf(fd, "uuid=${uuid_gpt_%s};", basename( isp_info.file_header.partition_info[i].file_name));
		}
		fprintf(fd, "name=userdata,size=-,uuid=${uuid_gpt_userdata}\"\n\n");

		fprintf(fd, "printenv partitions\n");
		fprintf(fd, "echo Writing GPT ...\n");
		fprintf(fd, "gpt write mmc 0 $partitions\n");

		fprintf(fd, "echo Initialize eMMC again due to GPT update ...\n");
		fprintf(fd, "mmc dev 0 && mmc rescan\n\n");
		fprintf(fd, "mmc part\n");

		// clear environment variables
		fprintf(fd, "\n");
		fprintf(fd, "setenv partitions\n");
		fprintf(fd, "setenv uuid_gpt_disk\n");
		for (i = 0; i < NUM_OF_PARTITION; i++) {
			if (isp_info.file_header.partition_info[i].file_name[0] == 0x00)
				break;
			fprintf(fd, "setenv uuid_gpt_%s\n", basename( isp_info.file_header.partition_info[i].file_name));
		}
		fprintf(fd, "setenv uuid_gpt_userdata\n");
		fprintf(fd, "\n");
	}
	int temp=0,k=0;
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (isp_info.file_header.partition_info[i].partition_size == 0)
			continue;

		fprintf(fd, "echo\n");
		fprintf(fd, "echo programming %s ...\n", basename( isp_info.file_header.partition_info[i].file_name));
		//uboot size is larger than 2M, only write xboot here
		if ((isp_info.file_header.partition_info[i].flags & FLAGS_BCH1K60)&&(i < IDX_PARTITION_UBOOT1)) {
				fprintf(fd, "fatload $isp_if $isp_dev $isp_ram_addr /%s 0x%x 0x%x\n", basename( isp_info.file_name_pack_image), isp_info.file_header.partition_info[i].file_size,
					isp_info.file_header.partition_info[i].file_offset);

#if !defined(REDUCE_MESSAGE)
			fprintf(fd, "md.b $isp_ram_addr 0x0100\n");
#endif

			idx_nand_write_bblk = get_partition_info_idx_by_file_name(isp_info.file_header.partition_info[i].file_name) -
					      get_partition_info_idx_by_file_name(isp_info.file_header.partition_info[IDX_PARTITION_XBOOT1].file_name);

			if (nand_or_emmc == IDX_NAND) {
				fprintf(fd, "setenv isp_nand_addr 0x${isp_addr_next}\n");
				fprintf(fd, "echo partition: %s, start from $isp_nand_addr, size: 0x%x, programmed size: 0x%x\n", basename( isp_info.file_header.partition_info[i].file_name),
					isp_info.file_header.partition_info[i].partition_size, isp_info.file_header.partition_info[i].file_size);
				// "nand erase.chip" already,
				// fprintf(fd, "nand erase $isp_nand_addr 0x%x\n", isp_info.file_header.partition_info[i].file_size);

				fprintf(fd, "setenv isp_nand_addr_write_bblk_%d $isp_nand_addr\n", idx_nand_write_bblk);

				snprintf(cmd, sizeof(cmd), "bblk write bblk $isp_ram_addr $isp_nand_addr 0x%x", isp_info.file_header.partition_info[i].file_size);
				fprintf(fd, "echo %s\n", cmd);
				fprintf(fd, "%s\n", cmd);
			} else if (nand_or_emmc == IDX_EMMC) {
				// fprintf(fd, "ispsp set_emmc_blk %s 0x0000\n", basename( isp_info.file_header.partition_info[i].file_name));
				// fprintf(fd, "mmc write $isp_ram_addr $isp_emmc_blk 0x%x\n", BYTE2BLOCK(isp_info.file_header.partition_info[i].file_size));
#ifdef XBOOT1_IN_EMMC_BOOTPART
				if (i == IDX_PARTITION_XBOOT1) {
					fprintf(fd, "echo XBOOT1: to eMMC boot partition\n");
					fprintf(fd, "mmc partconf 0 0 7 1\n");
					fprintf(fd, "mmc write $isp_ram_addr 0x0000 0x%x\n",
						BYTE2BLOCK(isp_info.file_header.partition_info[i].file_size));
					fprintf(fd, "mmc partconf 0 0 0 0\n");
				} else {
					fprintf(fd, "mmc write $isp_ram_addr 0x%x 0x%x\n",
						isp_info.file_header.partition_info[i].emmc_partition_start,
						BYTE2BLOCK(isp_info.file_header.partition_info[i].file_size));

				}
#else
				fprintf(fd, "mmc write $isp_ram_addr 0x%x 0x%x\n",
					isp_info.file_header.partition_info[i].emmc_partition_start,
					BYTE2BLOCK(isp_info.file_header.partition_info[i].file_size));
#endif
			}

			// fprintf(fd, "ispsp progress 0x%x 0x00\n", isp_info.file_header.partition_info[i].file_size);

			if (nand_or_emmc == IDX_NAND) {
				if (!(isp_info.file_header.partition_info[i].flags & FLAGS_BCH1K60_LAST_ONE)) {
					fprintf(fd, "setexpr isp_mtdpart_size ${isp_addr_next} - ${isp_nand_addr} && ");
					fprintf(fd, "setenv isp_mtdpart_size 0x${isp_mtdpart_size}\n");

#if !defined(REDUCE_MESSAGE)
					snprintf(cmd, sizeof(cmd), "mtdparts add nand0 ${isp_mtdpart_size}@${isp_nand_addr} %s && mtdparts && printenv mtdparts", basename( isp_info.file_header.partition_info[i].file_name));
#else
					snprintf(cmd, sizeof(cmd), "mtdparts add nand0 ${isp_mtdpart_size}@${isp_nand_addr} %s && printenv mtdparts", basename( isp_info.file_header.partition_info[i].file_name));
#endif

					fprintf(fd, "echo %s\n", cmd);
					fprintf(fd, "%s\n\n", cmd);
				} else {
					// Should keep some space after argv[ARGC_PACK_IMAGE_UBOOT2_FILE],
					// becaues argv[ARGC_PACK_IMAGE_XBOOT1_FILE] argv[ARGC_PACK_IMAGE_UBOOT1_FILE] won't be updated.
#ifdef ADDRESS_NAND_1ST_PARTITION
					fprintf(fd, "setenv isp_nand_addr_1st_part 0x%x\n\n", ADDRESS_NAND_1ST_PARTITION);
#else
					fprintf(fd, "setexpr isp_nand_addr_1st_part $isp_addr_next + 0x%x && setenv isp_nand_addr_1st_part 0x${isp_nand_addr_1st_part}\n\n",
						isp_info.file_header.partition_info[i].partition_size);
#endif
					fprintf(fd, "setexpr isp_mtdpart_size ${isp_nand_addr_1st_part} - ${isp_nand_addr} && ");
					fprintf(fd, "setenv isp_mtdpart_size 0x${isp_mtdpart_size}\n");

#if !defined(REDUCE_MESSAGE)
					snprintf(cmd, sizeof(cmd), "mtdparts add nand0 ${isp_mtdpart_size}@${isp_nand_addr} %s && mtdparts && printenv mtdparts", basename( isp_info.file_header.partition_info[i].file_name));
#else
					snprintf(cmd, sizeof(cmd), "mtdparts add nand0 ${isp_mtdpart_size}@${isp_nand_addr} %s && printenv mtdparts", basename( isp_info.file_header.partition_info[i].file_name));
#endif

					fprintf(fd, "echo %s\n", cmd);
					fprintf(fd, "%s\n\n", cmd);

					fprintf(fd, "printenv isp_nand_addr_1st_part\n\n");
				}
			}
		} else {
			if (nand_or_emmc == IDX_NAND) {
#if !defined(PARTITION_SIZE_BAD_BLOCK_DOES_NOT_COUNT)
				fprintf(fd, "setexpr isp_nand_addr $isp_nand_addr_1st_part + 0x%x && setenv isp_nand_addr 0x${isp_nand_addr}\n", isp_info.file_header.partition_info[i].partition_start_addr);
				fprintf(fd, "echo partition: %s, start from $isp_nand_addr, size: 0x%x, programmed size: 0x%x\n", basename( isp_info.file_header.partition_info[i].file_name),
					isp_info.file_header.partition_info[i].partition_size, isp_info.file_header.partition_info[i].file_size);
#else /* defined(PARTITION_SIZE_BAD_BLOCK_DOES_NOT_COUNT) */
				if (isp_info.file_header.partition_info[i].partition_start_addr == 0) {
					fprintf(fd, "ispsp find_area $isp_nand_addr_1st_part 0x%x\n", isp_info.file_header.partition_info[i].partition_size);
				} else {
					fprintf(fd, "ispsp find_area $isp_find_area_end 0x%x\n",      isp_info.file_header.partition_info[i].partition_size);
				}
				fprintf(fd, "setenv isp_nand_addr $isp_find_area_start && setenv isp_nand_addr 0x${isp_nand_addr}\n");
				fprintf(fd, "echo partition: %s, start from $isp_nand_addr, size: 0x${isp_find_area_new_size}, programmed size: 0x%x\n", basename( isp_info.file_header.partition_info[i].file_name),
					isp_info.file_header.partition_info[i].file_size);
#endif /* PARTITION_SIZE_BAD_BLOCK_DOES_NOT_COUNT */

				file_size = isp_info.file_header.partition_info[i].file_size;
				flag_first = 1;     // first MAX_MEM_SIZE_FOR_ISP bytes of the programmed file.
				size_programmed = 0;
				while (file_size) {
					size = (file_size > MAX_MEM_SIZE_FOR_ISP) ? MAX_MEM_SIZE_FOR_ISP : file_size;
					fprintf(fd, "fatload $isp_if $isp_dev $isp_ram_addr /%s 0x%x 0x%x\n", basename( isp_info.file_name_pack_image), size,
						(size_programmed + (isp_info.file_header.partition_info[i].file_offset)));

#if !defined(REDUCE_MESSAGE)
					fprintf(fd, "md.b $isp_ram_addr 0x0100\n");
#endif

					// "nand erase.chip" already,
					// fprintf(fd, "nand erase $isp_nand_addr 0x%x\n", isp_info.file_header.partition_info[i].file_size);
					// fprintf(fd, "setexpr isp_addr_nand_write_next $isp_nand_addr + 0x%x && setenv isp_addr_nand_write_next 0x${isp_addr_nand_write_next}\n",size_programmed);
					fprintf(fd, "echo isp_addr_nand_write_next: ${isp_addr_nand_write_next}\n");

					if (flag_first) {
						flag_first = 0;
						snprintf(cmd, sizeof(cmd), "nand write $isp_ram_addr $isp_nand_addr 0x%x", size);
					} else
						snprintf(cmd, sizeof(cmd), "nand write $isp_ram_addr $isp_addr_nand_write_next 0x%x",
							 size);    // isp_addr_nand_write_next is set in nand_write() (uboot/drivers/mtd/nand/nand_base.c)

					fprintf(fd, "echo %s\n", cmd);
					fprintf(fd, "%s\n", cmd);
					// fprintf(fd, "ispsp progress 0x%x 0x00\n", size);
					size_programmed += size;
					file_size       -= size;
				}

#if !defined(PARTITION_SIZE_BAD_BLOCK_DOES_NOT_COUNT)
				fprintf(fd, "setenv isp_mtdpart_size 0x%x\n", isp_info.file_header.partition_info[i].partition_size);
				snprintf(cmd, sizeof(cmd), "mtdparts add nand0 ${isp_mtdpart_size}@${isp_nand_addr} %s", basename( isp_info.file_header.partition_info[i].file_name));
#else /* defined(PARTITION_SIZE_BAD_BLOCK_DOES_NOT_COUNT) */
				fprintf(fd, "setenv isp_mtdpart_size 0x${isp_find_area_new_size}\n");
				fprintf(fd, "setenv isp_find_area_start 0x${isp_find_area_start}\n");
				snprintf(cmd, sizeof(cmd), "mtdparts add nand0 ${isp_mtdpart_size}@${isp_find_area_start} %s", basename( isp_info.file_header.partition_info[i].file_name));
#endif /* PARTITION_SIZE_BAD_BLOCK_DOES_NOT_COUNT */
				fprintf(fd, "echo %s\n", cmd);
				fprintf(fd, "%s\n", cmd);
				fprintf(fd, "if test \"$?\" = 0 ; then\n");

#if !defined(REDUCE_MESSAGE)
				fprintf(fd, "    mtdparts && printenv mtdparts\n");
#else
				fprintf(fd, "    printenv mtdparts\n");
#endif

				fprintf(fd, "else\n");
				fprintf(fd, "    exit -1\n");
				fprintf(fd, "fi\n\n");
			} else if (nand_or_emmc == IDX_EMMC) {

				// write kernel data to uboot part	for none-os!!!!!
				if (strncmp(isp_info.file_header.partition_info[i].file_name,"uboot",strlen("uboot"))==0)
				{
					for (k = 0; k < NUM_OF_PARTITION; k++)
					{
						if (strcmp(isp_info.file_header.partition_info[k].file_name,"kernel")==0)
						{
							file_size = isp_info.file_header.partition_info[k].file_size;
							file_offset = isp_info.file_header.partition_info[k].file_offset;
						}
					}
				}
				else
				{
					file_size = isp_info.file_header.partition_info[i].file_size;
					file_offset = isp_info.file_header.partition_info[i].file_offset;
				}
			
				size_programmed = 0;
				if (file_size == 0) {
					// fprintf(fd, "ispsp set_emmc_blk %s 0x%x\n", basename( isp_info.file_header.partition_info[i].file_name), BYTE2BLOCK(size_programmed));
					fprintf(fd, "mw.b $isp_ram_addr 0xFF 0x0800\n");     // fill 0xFF for 2KB.
					// fprintf(fd, "mmc write $isp_ram_addr $isp_emmc_blk 0x%x\n", BYTE2BLOCK(0x0800));
					fprintf(fd, "mmc write $isp_ram_addr 0x%x 0x%x\n",
						isp_info.file_header.partition_info[i].emmc_partition_start + BYTE2BLOCK(size_programmed),
						BYTE2BLOCK(0x0800));
				}
				
				while (file_size) {
					size = (file_size > MAX_MEM_SIZE_FOR_ISP) ? MAX_MEM_SIZE_FOR_ISP : file_size;
					fprintf(fd, "fatload $isp_if $isp_dev $isp_ram_addr /%s 0x%x 0x%x\n", basename( isp_info.file_name_pack_image), size,
						(size_programmed + file_offset));

#if !defined(REDUCE_MESSAGE)
					fprintf(fd, "md.b $isp_ram_addr 0x0100\n");
#endif
					// fprintf(fd, "ispsp set_emmc_blk %s 0x%x\n", basename( isp_info.file_header.partition_info[i].file_name), BYTE2BLOCK(size_programmed));
					// fprintf(fd, "mmc write $isp_ram_addr $isp_emmc_blk 0x%x\n", BYTE2BLOCK(size));
					fprintf(fd, "mmc write $isp_ram_addr 0x%x 0x%x\n",
						isp_info.file_header.partition_info[i].emmc_partition_start + BYTE2BLOCK(size_programmed),
						BYTE2BLOCK(size));
					// fprintf(fd, "ispsp progress 0x%x 0x00\n", size);
					size_programmed += size;
					file_size       -= size;
				}
			}
		}
	}
	if ((isp_info.file_header.flags & FLAGS_MTD_ONLY) && (nand_or_emmc == IDX_NAND)) {
#if !defined(REDUCE_MESSAGE)
		fprintf(fd, "mtdparts add nand0 - userdata && mtdparts && printenv mtdparts\n\n");      // Assign unused area to "userdata"
#else
		fprintf(fd, "mtdparts add nand0 - userdata && printenv mtdparts\n\n");                  // Assign unused area to "userdata"
#endif
	}

#ifdef PROGRAM_HEADER_LAST
	if (nand_or_emmc == IDX_NAND) {
		fprintf(fd, "\necho programming header (boot parameters) ...\n");
		idx = get_partition_info_idx_by_file_name(isp_info.file_header.partition_info[IDX_PARTITION_XBOOT1].file_name);
		if (idx < 0) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}

		// note: $isp_addr_next is changed after "nand write.bhdr"
		fprintf(fd, "bblk write bhdr auto 0 0x%x\n", isp_info.file_header.partition_info[idx].file_size);
	}
#endif

	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (isp_info.file_header.partition_info[i].partition_size == 0)
			continue;

		flag_first = 1;     // first MAX_MEM_SIZE_FOR_ISP bytes of the verified file.
		size_verified = 0;

		fprintf(fd, "echo\n");
		fprintf(fd, "echo verifying %s ...\n", basename( isp_info.file_header.partition_info[i].file_name));
		temp = i;
		if (strncmp(isp_info.file_header.partition_info[i].file_name,"uboot",strlen("uboot"))==0)
		{
			for (k = 0; k < NUM_OF_PARTITION; k++)
			{
				if (strcmp(isp_info.file_header.partition_info[k].file_name,"kernel")==0)
				{
					i = k;
				}
			}
		}
		
		file_size = isp_info.file_header.partition_info[i].file_size;
#if 0   // test, it would cause BCH error.
		char t[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
		int tflag = 0;
		if ((strcmp( basename( isp_info.file_header.partition_info[i].file_name), "spsdk") == 0) && (tflag == 0)) {
			printf("++++++hy test code++++++\n");
			fprintf(fd, "nand write 0x%x 0x017A0000 0x0A\n", &t);
			//fprintf(fd, "nand write 0x%x 0x017C0000 0x0A\n",&t);
			//fprintf(fd, "nand write 0x%x 0x017E0000 0x0A\n",&t);
			//fprintf(fd, "nand write 0x%x 0x01800000 0x0A\n",&t);
			//fprintf(fd, "nand write 0x%x 0x01820000 0x0A\n",&t);
			//fprintf(fd, "nand write 0x%x 0x01840000 0x0A\n",&t);
			tflag = 1;
		}
#endif

		while (file_size) {
			size = (file_size > MAX_MEM_SIZE_FOR_ISP) ? MAX_MEM_SIZE_FOR_ISP : file_size;

			if (nand_or_emmc == IDX_NAND) {
				if (isp_info.file_header.partition_info[i].flags & FLAGS_BCH1K60) {
					idx_nand_write_bblk = get_partition_info_idx_by_file_name(isp_info.file_header.partition_info[i].file_name) -
							      get_partition_info_idx_by_file_name(isp_info.file_header.partition_info[IDX_PARTITION_XBOOT1].file_name);

					if (file_size > MAX_MEM_SIZE_FOR_ISP) {
						printf("Error: %s: %d\n", __FILE__, __LINE__);
						exit(-1);
					}

#if !defined(NAND_READ_BY_PARTITION_NAME)
					snprintf(cmd, sizeof(cmd), "bblk read bblk $isp_ram_addr $isp_nand_addr_write_bblk_%d 0x%x", idx_nand_write_bblk, size);
#else
					snprintf(cmd, sizeof(cmd), "bblk read bblk $isp_ram_addr %s 0x%x", basename( isp_info.file_header.partition_info[i].file_name), size);
#endif
				} else {
					if (flag_first) {
						flag_first = 0;

#if !defined(NAND_READ_BY_PARTITION_NAME)
						fprintf(fd, "setexpr isp_nand_addr $isp_nand_addr_1st_part + 0x%x && setenv isp_nand_addr 0x${isp_nand_addr}\n", isp_info.file_header.partition_info[i].partition_start_addr);
						snprintf(cmd, sizeof(cmd), "nand read $isp_ram_addr $isp_nand_addr 0x%x", size);
#else
						fprintf(fd, "setexpr isp_nand_addr $isp_nand_addr_1st_part + 0x%x && setenv isp_nand_addr 0x${isp_nand_addr}\n", isp_info.file_header.partition_info[i].partition_start_addr);
						snprintf(cmd, sizeof(cmd), "nand read $isp_ram_addr %s 0x%x", basename( isp_info.file_header.partition_info[i].file_name), size);
#endif
					} else {
						// fprintf(fd, "setexpr isp_addr_nand_read_next $isp_nand_addr + 0x%x && setenv isp_addr_nand_read_next 0x${isp_addr_nand_read_next}\n", size_verified);
						fprintf(fd, "echo isp_addr_nand_read_next: ${isp_addr_nand_read_next}\n");
						snprintf(cmd, sizeof(cmd), "nand read $isp_ram_addr $isp_addr_nand_read_next 0x%x", size);  // isp_addr_nand_read_next is set in nand_write() (uboot/drivers/mtd/nand/nand_base.c)
					}
				}

				fprintf(fd, "echo %s\n", cmd);
				fprintf(fd, "%s\n", cmd);
			} else if (nand_or_emmc == IDX_EMMC) {
				// fprintf(fd, "ispsp set_emmc_blk %s 0x%x\n", basename( isp_info.file_header.partition_info[i].file_name), BYTE2BLOCK(size_verified));
				// fprintf(fd, "mmc read $isp_ram_addr $isp_emmc_blk 0x%x\n", BYTE2BLOCK(size));
#ifdef XBOOT1_IN_EMMC_BOOTPART
				if (i == IDX_PARTITION_XBOOT1) {
					fprintf(fd, "echo XBOOT1: from eMMC boot partition\n");
					fprintf(fd, "mmc partconf 0 0 7 1\n");
					fprintf(fd, "mmc read $isp_ram_addr 0x0000x 0x%x\n",
						BYTE2BLOCK(size));
					fprintf(fd, "mmc partconf 0 0 0 0\n");
				} else {
					fprintf(fd, "mmc read $isp_ram_addr 0x%x 0x%x\n",
						isp_info.file_header.partition_info[i].emmc_partition_start + BYTE2BLOCK(size_verified),
						BYTE2BLOCK(size));
				}
#else
				fprintf(fd, "mmc read $isp_ram_addr 0x%x 0x%x\n",
					isp_info.file_header.partition_info[i].emmc_partition_start + BYTE2BLOCK(size_verified),
					BYTE2BLOCK(size));
#endif
			}
			fprintf(fd, "echo md5 sum isp_info.full_file_name[%d]=%s\n",i, isp_info.full_file_name[i]);
			fprintf(fd, "md5sum $isp_ram_addr 0x%x md5sum_value\n", size);
			md5sum(isp_info.full_file_name[i], size_verified, size, cmd);
			fprintf(fd, "if test \"$md5sum_value\" = %s ; then\n", cmd);
			fprintf(fd, "    echo md5sum: OK.\n");
			fprintf(fd, "else\n");
			fprintf(fd, "    echo md5sum: Error!\n");
			fprintf(fd, "    exit -1\n");
			fprintf(fd, "fi\n\n");
			// fprintf(fd, "ispsp progress 0x00 0x%x\n\n", size);

			size_verified += size;
			file_size     -= size;
		}
		i = temp;
	}
	fprintf(fd, "\n");

	compile_string_for_part_sizes(uboot_var_part_sizes, sizeof(uboot_var_part_sizes));
	fprintf(fd, "setenv part_sizes %s\n\n", uboot_var_part_sizes);

	// fprintf(fd, "ispsp mtdparts_adj\n");

#if 0
	if (nand_or_emmc == IDX_EMMC) {
		fprintf(fd, "ispsp set_emmc_part_info\n");
	}
#endif

	// Remove environment variables that aren't requried to save.
	fprintf(fd, "setenv isp_addr_next\n");
	fprintf(fd, "setenv isp_addr_nand_read_next\n");
	fprintf(fd, "setenv isp_addr_nand_write_next\n");
	// fprintf(fd, "setenv isp_block_size\n");
	fprintf(fd, "setenv isp_ram_addr\n");
	fprintf(fd, "setenv isp_mtdpart_size\n");
	fprintf(fd, "setenv isp_nand_addr\n");
	fprintf(fd, "setenv isp_nand_addr_1st_part\n");
	fprintf(fd, "setenv isp_nand_addr_write_bblk_0\n");
	fprintf(fd, "setenv isp_nand_addr_write_bblk_1\n");
	fprintf(fd, "setenv isp_nand_addr_write_bblk_2\n");
	fprintf(fd, "setenv isp_size_total\n");
	fprintf(fd, "setenv md5sum_value\n");
	fprintf(fd, "setenv script_addr\n");
	fprintf(fd, "setenv isp_image_header_offset\n");
#if defined(PARTITION_SIZE_BAD_BLOCK_DOES_NOT_COUNT)
	fprintf(fd, "setenv isp_find_area_start\n");
	fprintf(fd, "setenv isp_find_area_new_size\n");
	fprintf(fd, "setenv isp_find_area_end\n");
#endif
	fprintf(fd, "setenv isp_main_storage\n");
	// fprintf(fd, "setenv isp_emmc_blk\n");

	fprintf(fd, "echo Following environment variables will be saved:\n");
	fprintf(fd, "printenv\n");
	fprintf(fd, "env save\n");
	if (nand_or_emmc == IDX_EMMC) {
		fprintf(fd, "env save\n"); /* eMMC: save to env_redund */
	}

	fprintf(fd, "echo **************************************************\n");
	fprintf(fd, "echo               ISP all: Done                       \n");
	fprintf(fd, "echo **************************************************\n");
	fprintf(fd, "setenv isp_all_or_update_done 0x01\n");

	fclose(fd);

#if !defined(MESSAGE_OUT_NONE)
	printf("\nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
	sprintf(cmd, "cat %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);
	printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");
#endif

	sprintf(cmd, "mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n \"ISP script for %s\" -d %s %s %s", isp_info.file_name_pack_image, tmp_file, file_name_isp_script,
		MESSAGE_OUT);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "rm -f %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);

	return 0;
}

int pack_image(int argc, char **argv)
{
	FILE *fd;
	int i, j;
	struct stat file_stat;
	char tmp_file[32], tmp_file2[32], tmp_file3[32], file_name_isp_script[NUM_STORAGE][32], cmd[1024];
	u32 tmp_u32, isp_script_size[NUM_STORAGE], file_offset_isp_script[NUM_STORAGE];
	u32 offset_of_last_file;
	u32 next_partition_start_address;
	int idx_last_info_of_binary_partition = argc;
	int idx_partition_info;


	if (sizeof(struct partition_info_s) != SIZE_PARTITION_INFO_S) {
		printf("Expect sizeof(partition_info) == %d\n", SIZE_PARTITION_INFO_S);
		exit(-1);
	}

	sprintf(tmp_file, "tmp%08x", (u32)(getpid()));
	sprintf(tmp_file2, "tmp2%08x", (u32)(getpid()));
	sprintf(tmp_file3, "tmp3%08x", (u32)(getpid()));
	sprintf(file_name_isp_script[IDX_NAND], "isp_script_nand_%08x", (u32)(getpid()));
	sprintf(file_name_isp_script[IDX_EMMC], "isp_script_emmc_%08x", (u32)(getpid()));
	isp_info.file_header.flags |= FLAGS_MTD_ONLY;

	idx_partition_info = 0;
	offset_of_last_file = sizeof(isp_info.file_header);
	for (i = 0; i < argc; i++) {
		if (i <= ARGC_PACK_IMAGE_SUBCMD) {  // ARGC_PACK_IMAGE_MAINCMD, ARGC_PACK_IMAGE_SUBCMD
			continue;
		} else if (i == ARGC_PACK_IMAGE_OUTPUT) {
			if (strlen(argv[i]) > SIZE_FULL_FILE_NAME) {
				printf("Error: %s: %d\n", __FILE__, __LINE__);
				exit(-1);
			}
			strcpy(isp_info.file_name_pack_image, argv[i]);
			// strcpy(isp_info.base_file_name_pack_image, basename(argv[i]));

			fd = fopen(isp_info.file_name_pack_image, "wb");
			if (fd == NULL) {
				printf("Fail to open %s\n", isp_info.file_name_pack_image);
				exit(-1);
			}
			fclose(fd);
			sprintf(cmd, "rm %s", isp_info.file_name_pack_image);
			// printf("%s\n", cmd);
			system(cmd);
		} else if (i == ARGC_PACK_IMAGE_XBOOT0_FILE) {
			if (stat(argv[i], &file_stat) == 0) {
				tmp_u32 = (u32)(file_stat.st_size);
				tmp_u32 = ALIGN_TO_1K(tmp_u32);
				if (tmp_u32 > FILE_SIZE_IMAGE_XBOOT0) {
					printf("Error: xboot size large than %d\n", FILE_SIZE_IMAGE_XBOOT0);
					exit(-1);
				}
				truncate(argv[i], FILE_SIZE_IMAGE_XBOOT0);
				offset_of_last_file += FILE_SIZE_IMAGE_XBOOT0;
				if (strlen(argv[i]) > SIZE_FULL_FILE_NAME) {
					printf("Error: %s: %d\n", __FILE__, __LINE__);
					exit(-1);
				}
				strcpy(isp_info.full_file_name_xboot0, argv[i]);
			} else {
				printf("Error: %s: %d\n", __FILE__, __LINE__);
				exit(-1);
			}
		} else if (i == ARGC_PACK_IMAGE_UBOOT0_FILE) {
			if (stat(argv[i], &file_stat) == 0) {
				tmp_u32 = (u32)(file_stat.st_size);
				tmp_u32 = ALIGN_TO_1K(tmp_u32);
				if (tmp_u32 > FILE_SIZE_IMAGE_UBOOT0) {
					printf("Error: uboot size large than %d\n", FILE_SIZE_IMAGE_UBOOT0);
					exit(-1);
				}
				truncate(argv[i], FILE_SIZE_IMAGE_UBOOT0);
				offset_of_last_file += FILE_SIZE_IMAGE_UBOOT0;
				if (strlen(argv[i]) > SIZE_FULL_FILE_NAME) {
					printf("Error: %s: %d\n", __FILE__, __LINE__);
					exit(-1);
				}
				strcpy(isp_info.full_file_name_uboot0, argv[i]);
			} else {
				printf("Error: %s: %d\n", __FILE__, __LINE__);
				exit(-1);
			}
		} else if ((i >= ARGC_PACK_IMAGE_XBOOT1_FILE) && (i <= idx_last_info_of_binary_partition)) {
			if (strlen(argv[i]) > SIZE_FULL_FILE_NAME) {
				printf("Error: %s: %d\n", __FILE__, __LINE__);
				exit(-1);
			}
			strcpy(isp_info.full_file_name[idx_partition_info], argv[i]);

			strncpy(isp_info.file_header.partition_info[idx_partition_info].file_name, basename(argv[i]), SIZE_FILE_NAME);
			isp_info.file_header.partition_info[idx_partition_info].file_name[SIZE_FILE_NAME - 1] = 0x00;
			i++;
			if (i >= argc) {
				printf("Error: %s: %d\n", __FILE__, __LINE__);
				exit(-1);
			}
			isp_info.file_header.partition_info[idx_partition_info].partition_size = (u32)(strtoul(argv[i], NULL, 0));

			idx_partition_info++;
		}
	}

	for (i = 0; i < idx_partition_info; i++) {
#ifdef XBOOT1_IN_EMMC_BOOTPART
		if (i <= IDX_PARTITION_UBOOT1) {
#else
		if (i <= IDX_PARTITION_XBOOT1) {
#endif
			isp_info.file_header.partition_info[i].emmc_partition_start = BYTE2BLOCK(GPT_HEADER_SIZE);

		} else {
			isp_info.file_header.partition_info[i].emmc_partition_start =
				isp_info.file_header.partition_info[i - 1].emmc_partition_start +
				BYTE2BLOCK(isp_info.file_header.partition_info[i - 1].partition_size);
			
		}

		// printf("%s, emmc_partition_start: 0x%x\n", isp_info.file_header.partition_info[i].file_name, isp_info.file_header.partition_info[i].emmc_partition_start);
	}

	// Don't use argc/argv in pack_image() after here

	next_partition_start_address = 0;
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (isp_info.file_header.partition_info[i].file_name[0]) {
			if (stat(isp_info.full_file_name[i], &file_stat) != 0) {
#if !defined(MESSAGE_OUT_NONE)
				printf("File not found: %s\n", isp_info.full_file_name[i]);
#endif
				sprintf(cmd, "touch %s", isp_info.full_file_name[i]);   // create a zero-byte file for it
#if !defined(MESSAGE_OUT_NONE)
				printf("%s\n", cmd);
#endif
				system(cmd);
			}
			if (stat(isp_info.full_file_name[i], &file_stat) == 0) {
				tmp_u32 = (u32)(file_stat.st_size);
				tmp_u32 = ALIGN_TO_1K(tmp_u32);
				// printf("File size of %s is %u, extend it to %u\n", isp_info.full_file_name[i], ((u32)(file_stat.st_size)), tmp_u32);
				truncate(isp_info.full_file_name[i], tmp_u32);
				isp_info.file_header.partition_info[i].file_size = tmp_u32;

				if (isp_info.file_header.partition_info[i].partition_size < isp_info.file_header.partition_info[i].file_size) {
					printf("Error: Assgined partition size is less than the image size: %s, %u < %u\n",
					       isp_info.file_header.partition_info[i].file_name,
					       isp_info.file_header.partition_info[i].partition_size,
					       isp_info.file_header.partition_info[i].file_size);
					exit(-1);
				}

				isp_info.file_header.partition_info[i].file_offset = offset_of_last_file;
				offset_of_last_file += tmp_u32;
				md5sum(isp_info.full_file_name[i], 0, 0, isp_info.file_header.partition_info[i].md5sum);

				isp_info.file_header.partition_info[i].partition_start_addr = next_partition_start_address;
				if (i <= IDX_PARTITION_UBOOT2) { // IDX_PARTITION_XBOOT1, IDX_PARTITION_UBOOT1, IDX_PARTITION_UBOOT2
					// Don't change next_partition_start_address,
					// The start address of these partitions are dynamically calculated.

					isp_info.file_header.partition_info[i].flags |= FLAGS_BCH1K60;

					if (i == IDX_PARTITION_UBOOT2)
						isp_info.file_header.partition_info[i].flags |= FLAGS_BCH1K60_LAST_ONE;
					else
						isp_info.file_header.partition_info[i].flags |= FLAGS_NOT_ALLOWED_TO_UPDATE;
				} else {
					next_partition_start_address += isp_info.file_header.partition_info[i].partition_size;
				}
			} else {
				printf("Error: %s: %d\n", __FILE__, __LINE__);  // isp_info.full_file_name[i] doesn't exist or can't be created
				exit(-1);
			}
		}
	}

#if !defined(MESSAGE_OUT_NONE)
	dump_isp_info();
#endif

	gen_script_main(file_name_isp_script[IDX_NAND], IDX_NAND);
	if (stat(file_name_isp_script[IDX_NAND], &file_stat) == 0) {
		tmp_u32 = (u32)(file_stat.st_size);
		tmp_u32 = ALIGN_TO_1K(tmp_u32);
		// printf("File size of %s is %u, extend it to %u\n", file_name_isp_script[IDX_NAND], ((u32)(file_stat.st_size)), tmp_u32);
		truncate(file_name_isp_script[IDX_NAND], tmp_u32);
		isp_script_size[IDX_NAND] = tmp_u32;
		file_offset_isp_script[IDX_NAND] = offset_of_last_file;
		offset_of_last_file += tmp_u32;
	} else {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}

#ifdef SUPPORT_MAIN_STORAGE_IS_EMMC
	gen_script_main(file_name_isp_script[IDX_EMMC], IDX_EMMC);
	if (stat(file_name_isp_script[IDX_EMMC], &file_stat) == 0) {
		tmp_u32 = (u32)(file_stat.st_size);
		tmp_u32 = ALIGN_TO_1K(tmp_u32);
		// printf("File size of %s is %u, extend it to %u\n", file_name_isp_script[IDX_EMMC], ((u32)(file_stat.st_size)), tmp_u32);
		truncate(file_name_isp_script[IDX_EMMC], tmp_u32);
		isp_script_size[IDX_EMMC] = tmp_u32;
		file_offset_isp_script[IDX_EMMC] = offset_of_last_file;
		offset_of_last_file += tmp_u32;
	} else {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
#endif  /* SUPPORT_MAIN_STORAGE_IS_EMMC */

	/* concatenate all input files */
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (isp_info.file_header.partition_info[i].partition_size != 0) {
			sprintf(cmd, "cat %s >> %s", isp_info.full_file_name[i], tmp_file);
			// printf("%s\n", cmd);
			system(cmd);
		}
	}

	/* concatenate ISP script file */
	sprintf(cmd, "cat %s >> %s", file_name_isp_script[IDX_NAND], tmp_file);
	// printf("%s\n", cmd);
	system(cmd);
#ifdef SUPPORT_MAIN_STORAGE_IS_EMMC
	sprintf(cmd, "cat %s >> %s", file_name_isp_script[IDX_EMMC], tmp_file);
	// printf("%s\n", cmd);
	system(cmd);
#endif  /* SUPPORT_MAIN_STORAGE_IS_EMMC */

	/* isp_info.file_header.init_script[], it's init script to load ISP's main script */
	fd = fopen(tmp_file2, "wb");
	fprintf(fd, "if test \"$isp_if\" = usb ; then\n");
	fprintf(fd, "    echo ISP file from USB storage\n");
	fprintf(fd, "elif test \"$isp_if\" = mmc ; then\n");
	fprintf(fd, "    echo ISP file from SD Card\n");
	fprintf(fd, "else\n");
	fprintf(fd, "    echo set to USB device 0 and use memory area start from 0x%x\n", ADDRESS_FATLOAD);
	fprintf(fd, "    setenv isp_if usb\n");
	fprintf(fd, "    setenv isp_dev 0\n");
	fprintf(fd, "    setenv isp_ram_addr 0x%x\n", ADDRESS_FATLOAD);
	fprintf(fd, "    $isp_if start\n");
	fprintf(fd, "fi\n\n");
	fprintf(fd, "fatinfo $isp_if $isp_dev\n");
	fprintf(fd, "fatls   $isp_if $isp_dev /\n\n");

	fprintf(fd, "setenv isp_size_total 0x%lx\n", (file_offset_isp_script[IDX_NAND] - FILE_SIZE_IMAGE_XBOOT0 - FILE_SIZE_IMAGE_UBOOT0 - sizeof(isp_info.file_header)));
	// fprintf(fd, "ispsp progress 0x00 0x00\n\n");

	fprintf(fd, "if test \"$isp_force_to_abort\" = yes ; then\n");
	fprintf(fd, "    echo isp_force_to_abort\n");
	fprintf(fd, "    exit -1\n");
	fprintf(fd, "fi\n\n");

	fprintf(fd, "echo Load ISP main script and run it ...\n");
	fprintf(fd, "if test \"$isp_main_storage\" = nand ; then\n");
	fprintf(fd, "    fatload $isp_if $isp_dev $isp_ram_addr /%s 0x%x 0x%x\n", basename( isp_info.file_name_pack_image), isp_script_size[IDX_NAND], file_offset_isp_script[IDX_NAND]);
	fprintf(fd, "elif test \"$isp_main_storage\" = emmc ; then\n");
#ifdef SUPPORT_MAIN_STORAGE_IS_EMMC
	fprintf(fd, "    fatload $isp_if $isp_dev $isp_ram_addr /%s 0x%x 0x%x\n", basename( isp_info.file_name_pack_image), isp_script_size[IDX_EMMC], file_offset_isp_script[IDX_EMMC]);
#else
	fprintf(fd, "    echo Error\n");
	fprintf(fd, "    exit -1\n");
#endif  /* SUPPORT_MAIN_STORAGE_IS_EMMC */

	fprintf(fd, "else\n");
	fprintf(fd, "    setenv isp_main_storage nand\n");  // for U-Boot backward compatible, default to nand
	fprintf(fd, "    fatload $isp_if $isp_dev $isp_ram_addr /%s 0x%x 0x%x\n", basename( isp_info.file_name_pack_image), isp_script_size[IDX_NAND], file_offset_isp_script[IDX_NAND]);
	fprintf(fd, "fi\n");
	fprintf(fd, "source $isp_ram_addr\n");
	fclose(fd);

#if !defined(MESSAGE_OUT_NONE)
	printf("\nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
	sprintf(cmd, "cat %s", tmp_file2);
	// printf("%s\n", cmd);
	system(cmd);
	printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");

	printf("\nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
	printf("Should be able to run isp_info.file_header.init_script[] by:\n\n");
	printf("setenv isp_if usb && setenv isp_dev 0 && setenv isp_ram_addr 0x%x\n", ADDRESS_FATLOAD);
	printf("$isp_if start && fatload $isp_if $isp_dev $isp_ram_addr /%s 0x0800 0x%x && md.b $isp_ram_addr 0x0200\n", basename( isp_info.file_name_pack_image),
	       (FILE_SIZE_IMAGE_XBOOT0 + FILE_SIZE_IMAGE_UBOOT0));
	printf("setexpr script_addr $isp_ram_addr + 0x%x && setenv script_addr 0x${script_addr} && source $script_addr\n", ((u32)(offsetof(struct file_header_s, init_script))));
	printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");
#endif

	sprintf(cmd, "mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n \"Init ISP script\" -d %s %s %s", tmp_file2, tmp_file3, MESSAGE_OUT);
	// printf("%s\n", cmd);
	system(cmd);
	if (stat(tmp_file3, &file_stat) == 0) {
		if (file_stat.st_size > SIZE_INIT_SCRIPT) {
			printf("\n\n\nSIZE_INIT_SCRIPT is too small\n\n");
			exit(-1);
		}

		fd = fopen(tmp_file3, "rb");
		if (fread(isp_info.file_header.init_script, file_stat.st_size, 1, fd) != 1) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}
		fclose(fd);
	}

	fd = fopen(tmp_file2, "wb");
	if (fd == NULL) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
	if (fwrite(&isp_info.file_header, sizeof(isp_info.file_header), 1, fd) != 1) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
	fclose(fd);

	sprintf(cmd, "cat %s %s %s %s > %s", isp_info.full_file_name_xboot0, isp_info.full_file_name_uboot0, tmp_file2, tmp_file, isp_info.file_name_pack_image);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "rm -f %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);
	sprintf(cmd, "rm -f %s", tmp_file2);
	// printf("%s\n", cmd);
	system(cmd);
	sprintf(cmd, "rm -f %s", tmp_file3);
	// printf("%s\n", cmd);
	system(cmd);
	sprintf(cmd, "rm -f %s", file_name_isp_script[IDX_NAND]);
	// printf("%s\n", cmd);
	system(cmd);
	sprintf(cmd, "rm -f %s", file_name_isp_script[IDX_EMMC]);
	// printf("%s\n", cmd);
	system(cmd);

	printf("\nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (isp_info.file_header.partition_info[i].flags & FLAGS_BCH1K60_LAST_ONE)
			break;
	}
	for (/* i=i */; i < NUM_OF_PARTITION; i++) {
		if (isp_info.file_header.partition_info[i].partition_size == 0)
			break;

		printf("%-32s 0x%x/0x%x (%.02lf%%) used\n",
		       basename( isp_info.file_header.partition_info[i].file_name),
		       isp_info.file_header.partition_info[i].file_size, isp_info.file_header.partition_info[i].partition_size,
		       100 * (double)(isp_info.file_header.partition_info[i].file_size) / ((double)(isp_info.file_header.partition_info[i].partition_size)));
	}
	printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");

	return 0;
}


#define EXTRACT4UPDATE_FROM_STORAGE (0)
#define EXTRACT4UPDATE_FROM_TFTP    (1)

int extract4update(int argc, char **argv, int extract4update_src)
{
	FILE *fd, *fd2;
	struct file_header_s file_header_extract4update;
	int i, idx, num_need_update;
	char tmp_file[32], tmp_file_init_script[32], tmp_file_file_header[32], tmp_file_main_script[32], cmd[1024];
	u32 offset_of_last_file, isp_script_size, tmp_u32;
	struct stat file_stat;
	u32 size_nand_write, size_decrypt;
	u32 file_size, size, size_programmed, size_verified, size_processed;
	u32 flag_first;
	char uboot_var_part_sizes[2048];
	u32 tftpxxxx = 0;

	fd = fopen(argv[ARGC_EXTRACT4UPDATE_INPUT], "rb");
	if (fd == NULL) {
		printf("Error: Can't open %s\n", argv[ARGC_EXTRACT4UPDATE_INPUT]);
		exit(-1);
	}

	if (fseek(fd, FILE_SIZE_IMAGE_XBOOT0 + FILE_SIZE_IMAGE_UBOOT0, SEEK_SET) != 0) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}

	if (fread(&isp_info.file_header, sizeof(isp_info.file_header), 1, fd) != 1) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}

	fclose(fd);

	if (memcmp(isp_info.file_header.signature, file_header_signature, sizeof(file_header_signature)) != 0) {
		printf("Error: %s doesn't have correct signature.\n", argv[ARGC_EXTRACT4UPDATE_INPUT]);
		exit(-1);
	}

	compile_string_for_part_sizes(uboot_var_part_sizes, sizeof(uboot_var_part_sizes));
	// printf("uboot_var_part_sizes: %s\n", uboot_var_part_sizes);

	offset_of_last_file = sizeof(file_header_extract4update);
	memset(&file_header_extract4update, 0x00, sizeof(file_header_extract4update));
	strncpy(file_header_extract4update.signature, file_header_signature, sizeof(file_header_extract4update.signature));

	num_need_update = 0;
	for (i = ARGC_EXTRACT4UPDATE_PARTITION0; i < argc; i++) {
		idx = get_partition_info_idx_by_file_name(argv[i]);
		if ((idx < 0) ||
		    (isp_info.file_header.partition_info[idx].flags & FLAGS_NOT_ALLOWED_TO_UPDATE)) {
			printf("Error: %s isn't a valid partition.\n", argv[i]);
			exit(-1);
		}
		memcpy(&file_header_extract4update.partition_info[num_need_update], &isp_info.file_header.partition_info[idx], sizeof(struct partition_info_s));

		file_header_extract4update.partition_info[num_need_update].file_offset = offset_of_last_file;

		offset_of_last_file += file_header_extract4update.partition_info[num_need_update].file_size;
		num_need_update++;
	}
	// Here, offset_of_last_file is the offset for main script.

	// Prepare main script for update
	sprintf(tmp_file, "tmp%08x", (u32)(getpid()));
	fd2 = fopen(tmp_file, "wb");

	if (extract4update_src == EXTRACT4UPDATE_FROM_STORAGE) {
		// Do nothing, done in init script:
		// fprintf(fd2, "setenv isp_size_total 0x%lx\n", (offset_of_last_file - sizeof(file_header_extract4update)));
		// fprintf(fd2, "ispsp progress 0x00 0x00\n\n");
	} else if (extract4update_src == EXTRACT4UPDATE_FROM_TFTP) {
		fprintf(fd2, "setenv isp_size_total 0x%lx\n", (offset_of_last_file - sizeof(file_header_extract4update)));
		// fprintf(fd2, "ispsp progress 0x00 0x00\n\n");
	}

	fprintf(fd2, "echo Check original sizes of partitions ...\n");
	fprintf(fd2, "printenv part_sizes\n");
	fprintf(fd2, "if test \"$part_sizes\" = %s ; then\n", uboot_var_part_sizes);
	fprintf(fd2, "    echo part_sizes is as expected.\n");
	fprintf(fd2, "else\n");
	fprintf(fd2, "    echo part_sizes is different!\n");
	fprintf(fd2, "    echo new part_sizes = %s\n", uboot_var_part_sizes);
	fprintf(fd2, "    exit -1\n");
	fprintf(fd2, "fi\n\n");

	fprintf(fd2, "if test \"$isp_main_storage\" = nand ; then\n");
	fprintf(fd2, "    printenv isp_main_storage\n");
	fprintf(fd2, "elif test \"$isp_main_storage\" = emmc ; then\n");
#ifdef SUPPORT_MAIN_STORAGE_IS_EMMC
	fprintf(fd2, "    printenv isp_main_storage\n");
#else
	fprintf(fd2, "    echo Error\n");
	fprintf(fd2, "    exit -1\n");
#endif  /* SUPPORT_MAIN_STORAGE_IS_EMMC */
	fprintf(fd2, "else\n");
	fprintf(fd2, "    setenv isp_main_storage nand\n");  // for U-Boot backward compatible, default to nand
	fprintf(fd2, "fi\n");
	fprintf(fd2, "\n");

	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (file_header_extract4update.partition_info[i].partition_size == 0)
			continue;

		fprintf(fd2, "echo\n");
		fprintf(fd2, "echo Updating %s ...\n\n", basename( file_header_extract4update.partition_info[i].file_name));
		fprintf(fd2, "if test \"$isp_main_storage\" = nand ; then\n");
		fprintf(fd2, "    nand erase.part %s\n", basename( file_header_extract4update.partition_info[i].file_name));
		fprintf(fd2, "elif test \"$isp_main_storage\" = emmc ; then\n");
		fprintf(fd2, "    echo\n");
		fprintf(fd2, "fi\n");

		file_size = file_header_extract4update.partition_info[i].file_size;
		flag_first = 1;     // first MAX_MEM_SIZE_FOR_ISP bytes of the programmed file.
		size_programmed = 0;
		while (file_size) {
			size = (file_size > MAX_MEM_SIZE_FOR_ISP) ? MAX_MEM_SIZE_FOR_ISP : file_size;

			if (extract4update_src == EXTRACT4UPDATE_FROM_STORAGE) {
				fprintf(fd2, "setexpr isp_update_file_offset $isp_image_header_offset + 0x%x && setenv isp_update_file_offset 0x${isp_update_file_offset}\n",
					(size_programmed + file_header_extract4update.partition_info[i].file_offset));
				fprintf(fd2, "fatload $isp_if $isp_dev $isp_ram_addr $isp_update_file_name 0x%x $isp_update_file_offset\n", size);
			} else if (extract4update_src == EXTRACT4UPDATE_FROM_TFTP) {
				tftpxxxx++; // TFTP0000.BIN is script
				fprintf(fd2, "dhcp $isp_ram_addr $serverip:TFTP%04X.BIN\n", tftpxxxx);
			}

#if !defined(REDUCE_MESSAGE)
			fprintf(fd2, "md.b $isp_ram_addr 0x0100\n");
#endif
			if (flag_first) {
				flag_first = 0;
				snprintf(cmd, sizeof(cmd), "%s $isp_ram_addr %s 0x%x",
					 (file_header_extract4update.partition_info[i].flags & FLAGS_BCH1K60) ? "bblk write bblk" : "nand write",
					 basename( file_header_extract4update.partition_info[i].file_name), size);
			} else {
				if (file_header_extract4update.partition_info[i].flags & FLAGS_BCH1K60) {
					printf("Error: %s: %d\n", __FILE__, __LINE__);
					return -1;
				}

				snprintf(cmd, sizeof(cmd), "nand write $isp_ram_addr $isp_addr_nand_write_next 0x%x",
					 size);    // isp_addr_nand_write_next is set in nand_write() (uboot/drivers/mtd/nand/nand_base.c)
			}

			fprintf(fd2, "if test \"$isp_main_storage\" = nand ; then\n");
			fprintf(fd2, "    echo %s\n", cmd);
			fprintf(fd2, "    %s\n", cmd);
			fprintf(fd2, "elif test \"$isp_main_storage\" = emmc ; then\n");
#ifdef SUPPORT_MAIN_STORAGE_IS_EMMC
			// snprintf(cmd, sizeof(cmd), "ispsp set_emmc_blk %s 0x%x", basename( file_header_extract4update.partition_info[i].file_name), BYTE2BLOCK(size_programmed));
			// fprintf(fd2, "    echo %s\n", cmd);
			// fprintf(fd2, "    %s\n", cmd);
			// snprintf(cmd, sizeof(cmd), "mmc write $isp_ram_addr $isp_emmc_blk 0x%x", BYTE2BLOCK(size));
			snprintf(cmd, sizeof(cmd), "mmc write $isp_ram_addr 0x%x 0x%x",
				 file_header_extract4update.partition_info[i].emmc_partition_start + BYTE2BLOCK(size_programmed),
				 BYTE2BLOCK(size));
			fprintf(fd2, "    echo %s\n", cmd);
			fprintf(fd2, "    %s\n", cmd);
#else
			fprintf(fd2, "    echo\n");
#endif  /* SUPPORT_MAIN_STORAGE_IS_EMMC */

			fprintf(fd2, "fi\n");

			// fprintf(fd2, "ispsp progress 0x%x 0x00\n\n", size);
			size_programmed += size;
			file_size       -= size;
		}

		fprintf(fd2, "\nmw.b $isp_ram_addr 0x00 0x%x\n\n", MAX_MEM_SIZE_FOR_ISP);
		fprintf(fd2, "echo\n");
		fprintf(fd2, "echo Verifying %s ...\n\n", basename( file_header_extract4update.partition_info[i].file_name));

		file_size = file_header_extract4update.partition_info[i].file_size;
		flag_first = 1;     // first MAX_MEM_SIZE_FOR_ISP bytes of the programmed file.
		size_verified = 0;
		while (file_size) {
			size = (file_size > MAX_MEM_SIZE_FOR_ISP) ? MAX_MEM_SIZE_FOR_ISP : file_size;

			if (flag_first) {
				flag_first = 0;
				snprintf(cmd, sizeof(cmd), "%s $isp_ram_addr %s 0x%x",
					 (file_header_extract4update.partition_info[i].flags & FLAGS_BCH1K60) ? "bblk read bblk" : "nand read",
					 basename( file_header_extract4update.partition_info[i].file_name), size);
			} else {
				snprintf(cmd, sizeof(cmd), "nand read $isp_ram_addr $isp_addr_nand_read_next 0x%x", size);  // isp_addr_nand_read_next is set in nand_write() (uboot/drivers/mtd/nand/nand_base.c)
			}

			fprintf(fd2, "if test \"$isp_main_storage\" = nand ; then\n");
			fprintf(fd2, "    echo %s\n", cmd);
			fprintf(fd2, "    %s\n", cmd);
			fprintf(fd2, "elif test \"$isp_main_storage\" = emmc ; then\n");
#ifdef SUPPORT_MAIN_STORAGE_IS_EMMC
			// snprintf(cmd, sizeof(cmd), "ispsp set_emmc_blk %s 0x%x", basename( file_header_extract4update.partition_info[i].file_name), BYTE2BLOCK(size_verified));
			// fprintf(fd2, "    echo %s\n", cmd);
			// fprintf(fd2, "    %s\n", cmd);
			// snprintf(cmd, sizeof(cmd), "mmc read $isp_ram_addr $isp_emmc_blk 0x%x", BYTE2BLOCK(size));
			snprintf(cmd, sizeof(cmd), "mmc read $isp_ram_addr 0x%x 0x%x",
				 file_header_extract4update.partition_info[i].emmc_partition_start + BYTE2BLOCK(size_verified),
				 BYTE2BLOCK(size));
			fprintf(fd2, "    echo %s\n", cmd);
			fprintf(fd2, "    %s\n", cmd);
#else
			fprintf(fd2, "    echo\n", cmd);
#endif  /* SUPPORT_MAIN_STORAGE_IS_EMMC */
			fprintf(fd2, "fi\n");

			fprintf(fd2, "md5sum $isp_ram_addr 0x%x md5sum_value\n", size);
			idx = get_partition_info_idx_by_file_name(file_header_extract4update.partition_info[i].file_name);
			if (idx < 0) {
				printf("Error: %s: %d\n", __FILE__, __LINE__);
				exit(-1);
			}
			md5sum(argv[ARGC_EXTRACT4UPDATE_INPUT], (isp_info.file_header.partition_info[idx].file_offset + size_verified), size, cmd);
			fprintf(fd2, "if test \"$md5sum_value\" = %s ; then\n", cmd);
			fprintf(fd2, "    echo md5sum: OK.\n");
			fprintf(fd2, "else\n");
			fprintf(fd2, "    echo md5sum: Error!\n");
			fprintf(fd2, "    exit -1\n");
			fprintf(fd2, "fi\n\n");
			// fprintf(fd2, "ispsp progress 0x00 0x%x\n\n", size);

			size_verified += size;
			file_size     -= size;
		}
	}

	fprintf(fd2, "echo **************************************************\n");
	fprintf(fd2, "echo               ISP update: Done                    \n");
	fprintf(fd2, "echo **************************************************\n");
	fprintf(fd2, "setenv isp_all_or_update_done 0x01\n");

	fclose(fd2);

#if !defined(MESSAGE_OUT_NONE)
	printf("\nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
	sprintf(cmd, "cat %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);
	printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");
#endif

	sprintf(tmp_file_main_script, "tmp_file_main_script%08x", (u32)(getpid()));
	sprintf(cmd, "mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n \"main update script\" -d %s %s %s", tmp_file, tmp_file_main_script, MESSAGE_OUT);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "rm -f %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);

	if (stat(tmp_file_main_script, &file_stat) == 0) {
		tmp_u32 = (u32)(file_stat.st_size);
		tmp_u32 = ALIGN_TO_1K(tmp_u32);
		// printf("File size of %s is %u, extend it to %u\n", tmp_file_main_script, ((u32)(file_stat.st_size)), tmp_u32);
		truncate(tmp_file_main_script, tmp_u32);
		isp_script_size = tmp_u32;
	} else {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}

	/* file_header.init_script[], it's init script to load ISP's main script */
	// Because XBoot can't be updated, use it as signature.
	sprintf(tmp_file, "tmp%08x", (u32)(getpid()));
	fd2 = fopen(tmp_file, "wb");

	// fprintf(fd2, "echo verifying %s ...\n", basename( file_header.partition_info[0].file_name));
	// snprintf(cmd, sizeof(cmd), "nand read.bblk $isp_ram_addr %s 0x%x", basename( file_header.partition_info[0].file_name), file_header.partition_info[0].file_size);
	// fprintf(fd2, "echo %s\n", cmd);
	// fprintf(fd2, "%s\n", cmd);
	//
	// fprintf(fd2, "md5sum $isp_ram_addr 0x%x md5sum_value\n", file_header.partition_info[0].file_size);
	// fprintf(fd2, "if test \"$md5sum_value\" = %s ; then\n", file_header.partition_info[0].md5sum);
	// fprintf(fd2, "    echo md5sum: OK.\n");
	// fprintf(fd2, "else\n");
	// fprintf(fd2, "    echo md5sum: Error!\n");
	// fprintf(fd2, "    exit -1\n");
	// fprintf(fd2, "fi\n\n");

	fprintf(fd2, "if test \"$isp_if\" = usb ; then\n");
	fprintf(fd2, "    echo ISP file from USB storage\n");
	fprintf(fd2, "elif test \"$isp_if\" = mmc ; then\n");
	fprintf(fd2, "    echo ISP file from SD Card\n");
	fprintf(fd2, "else\n");
	fprintf(fd2, "    echo set to USB device 0 and use memory area start from 0x%x\n", ADDRESS_FATLOAD);
	fprintf(fd2, "    setenv isp_if usb\n");
	fprintf(fd2, "    setenv isp_dev 0\n");
	fprintf(fd2, "    setenv isp_ram_addr 0x%x\n", ADDRESS_FATLOAD);
	fprintf(fd2, "    $isp_if start\n");
	fprintf(fd2, "fi\n\n");
	fprintf(fd2, "fatinfo $isp_if $isp_dev\n");
	fprintf(fd2, "fatls   $isp_if $isp_dev /\n\n");
	fprintf(fd2, "setenv isp_size_total 0x%lx\n", (offset_of_last_file - sizeof(file_header_extract4update)));
	// fprintf(fd2, "ispsp progress 0x00 0x00\n\n");
	fprintf(fd2, "echo Load main update script and run it ...\n");
	fprintf(fd2, "setexpr isp_update_file_offset $isp_image_header_offset + 0x%x && setenv isp_update_file_offset 0x${isp_update_file_offset}\n", offset_of_last_file);
	fprintf(fd2, "fatload $isp_if $isp_dev $isp_ram_addr $isp_update_file_name 0x%x $isp_update_file_offset\n", isp_script_size);

#if !defined(REDUCE_MESSAGE)
	fprintf(fd2, "md.b $isp_ram_addr 0x0200 && source $isp_ram_addr\n");
#else
	fprintf(fd2, "md.b $isp_ram_addr 0x0080 && source $isp_ram_addr\n");
#endif

	fclose(fd2);

#if !defined(MESSAGE_OUT_NONE)
	printf("\nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
	sprintf(cmd, "cat %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);
	printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");
#endif

	sprintf(tmp_file_init_script, "tmp_file_init_script%08x", (u32)(getpid()));
	sprintf(cmd, "mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n \"Init update script\" -d %s %s %s", tmp_file, tmp_file_init_script, MESSAGE_OUT);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "rm -f %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);

	if (stat(tmp_file_init_script, &file_stat) == 0) {
		if (file_stat.st_size > SIZE_INIT_SCRIPT) {
			printf("\n\n\nSIZE_INIT_SCRIPT is too small\n\n");
			exit(-1);
		}

		fd2 = fopen(tmp_file_init_script, "rb");
		if (fread(file_header_extract4update.init_script, file_stat.st_size, 1, fd2) != 1) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}
		fclose(fd2);
	}

	sprintf(cmd, "rm -f %s", tmp_file_init_script);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(tmp_file_file_header, "tmp_file_file_header%08x", (u32)(getpid()));

	fd2 = fopen(tmp_file_file_header, "wb");
	if (fd2 == NULL) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
	if (fwrite(&file_header_extract4update, sizeof(file_header_extract4update), 1, fd2) != 1) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
	fclose(fd2);

	/* concatenate files */
	if (extract4update_src == EXTRACT4UPDATE_FROM_STORAGE) {
		sprintf(cmd, "cat %s > %s", tmp_file_file_header, argv[ARGC_EXTRACT4UPDATE_OUTPUT]);
		// printf("%s\n", cmd);
		system(cmd);
	} else if (extract4update_src == EXTRACT4UPDATE_FROM_TFTP) {
		sprintf(cmd, "rm -rf %s && mkdir -p %s", argv[ARGC_EXTRACT4UPDATE_OUTPUT], argv[ARGC_EXTRACT4UPDATE_OUTPUT]);
		// printf("%s\n", cmd);
		system(cmd);
	}

	tftpxxxx = 0;
	tftpxxxx++; // TFTP0000.BIN is script
	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (file_header_extract4update.partition_info[i].file_size == 0)
			continue;

		idx = get_partition_info_idx_by_file_name(file_header_extract4update.partition_info[i].file_name);
		if (idx < 0) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}
		sprintf(tmp_file, "tmp%08x", (u32)(getpid()));
		sprintf(cmd, "dd if=%s of=%s bs=1024 skip=%u count=%u %s", argv[ARGC_EXTRACT4UPDATE_INPUT], tmp_file,
			(isp_info.file_header.partition_info[idx].file_offset >> 10),
			(file_header_extract4update.partition_info[i].file_size >> 10), MESSAGE_OUT);

		// printf("%s\n", cmd);
		system(cmd);

		if (extract4update_src == EXTRACT4UPDATE_FROM_STORAGE) {
			sprintf(cmd, "cat %s >> %s", tmp_file, argv[ARGC_EXTRACT4UPDATE_OUTPUT]);
			// printf("%s\n", cmd);
			system(cmd);
		} else if (extract4update_src == EXTRACT4UPDATE_FROM_TFTP) {
			size_processed = 0;
			file_size = file_header_extract4update.partition_info[i].file_size;
			while (file_size) {
				size = (file_size > MAX_MEM_SIZE_FOR_ISP) ? MAX_MEM_SIZE_FOR_ISP : file_size;
				sprintf(cmd, "dd if=%s of=%s/TFTP%04X.BIN bs=1024 skip=%u count=%u %s", tmp_file,
					argv[ARGC_EXTRACT4UPDATE_OUTPUT], tftpxxxx,
					(size_processed >> 10), (size >> 10), MESSAGE_OUT);
				// printf("%s\n", cmd);
				system(cmd);
				tftpxxxx++;

				file_size -= size;
				size_processed += size;
			}
		}

		sprintf(cmd, "rm -f %s", tmp_file);
		// printf("%s\n", cmd);
		system(cmd);
	}

	if (extract4update_src == EXTRACT4UPDATE_FROM_STORAGE) {
		sprintf(cmd, "cat %s >> %s", tmp_file_main_script, argv[ARGC_EXTRACT4UPDATE_OUTPUT]);
		// printf("%s\n", cmd);
		system(cmd);
	} else if (extract4update_src == EXTRACT4UPDATE_FROM_TFTP) {
		sprintf(cmd, "cat %s > %s/TFTP0000.BIN", tmp_file_main_script, argv[ARGC_EXTRACT4UPDATE_OUTPUT]);
		// printf("%s\n", cmd);
		system(cmd);
	}

	sprintf(cmd, "rm -f %s", tmp_file_file_header);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "rm -f %s", tmp_file_main_script);
	// printf("%s\n", cmd);
	system(cmd);

	return 0;
}

#define EXTRACT4BOOT2LINUX_FOR_SDCARD	(0)
#define EXTRACT4BOOT2LINUX_FOR_OTHER	(1)

int extract4boot2linux(int argc, char **argv,int extrac4boot2linux_src)
{
	FILE *fd, *fd2;
	struct file_header_s file_header_extract4boot2linux;
	const char *partition_to_be_loaded[] = {"kernel", "dtb", "rootfs", NULL};
	int i, idx;
	const char *char_ptr;
	int num_need_cp;
	u32 offset_of_last_file;
	char tmp_file[32], cmd[1024];
	char tmp_file_init_script[32];
	struct stat file_stat;
	char tmp_file_file_header[32];

	fd = fopen(argv[ARGC_EXTRACT4BOOT2LINUX_INPUT], "rb");
	if (fd == NULL) {
		printf("Error: Can't open %s\n", argv[ARGC_EXTRACT4BOOT2LINUX_INPUT]);
		exit(-1);
	}

	if (fseek(fd, FILE_SIZE_IMAGE_XBOOT0 + FILE_SIZE_IMAGE_UBOOT0, SEEK_SET) != 0) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}

	if (fread(&isp_info.file_header, sizeof(isp_info.file_header), 1, fd) != 1) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}

	fclose(fd);

	if (memcmp(isp_info.file_header.signature, file_header_signature, sizeof(file_header_signature)) != 0) {
		printf("Error: %s doesn't have correct signature.\n", argv[ARGC_EXTRACT4BOOT2LINUX_INPUT]);
		exit(-1);
	}

	offset_of_last_file = FILE_SIZE_IMAGE_XBOOT0 + FILE_SIZE_IMAGE_UBOOT0 + sizeof(file_header_extract4boot2linux);
	memset(&file_header_extract4boot2linux, 0x00, sizeof(file_header_extract4boot2linux));
	strncpy(file_header_extract4boot2linux.signature, file_header_signature, sizeof(file_header_extract4boot2linux.signature));

	num_need_cp = 0;
	while (1) {
		char_ptr = partition_to_be_loaded[num_need_cp];
		if (char_ptr == NULL) {
			break;
		}
		if(extrac4boot2linux_src == EXTRACT4BOOT2LINUX_FOR_SDCARD && (strcmp(char_ptr, "rootfs") == 0)){
			num_need_cp++;
			continue; // sdcard boot no need to loader rootfs
		}		
		
		// printf("%s, %d, %s\n", __FILE__, __LINE__, partition_to_be_loaded[num_need_cp]);

		idx = get_partition_info_idx_by_file_name((char *)partition_to_be_loaded[num_need_cp]);
		if (idx < 0) {
			printf("Warning: %s isn't a valid partition.\n", partition_to_be_loaded[num_need_cp]);
			break;
		}

		memcpy(&file_header_extract4boot2linux.partition_info[num_need_cp], &isp_info.file_header.partition_info[idx], sizeof(struct partition_info_s));
		file_header_extract4boot2linux.partition_info[num_need_cp].file_offset = offset_of_last_file;
		offset_of_last_file += file_header_extract4boot2linux.partition_info[num_need_cp].file_size;

		num_need_cp++;
	}

	sprintf(tmp_file, "tmp%08x", (u32)(getpid()));
	fd2 = fopen(tmp_file, "wb");

	fprintf(fd2, "if test \"$isp_if\" = usb ; then\n");
	fprintf(fd2, "    echo Boot from USB storage\n");
	fprintf(fd2, "elif test \"$isp_if\" = mmc ; then\n");
	fprintf(fd2, "    echo Boot from SD Card\n");
	fprintf(fd2, "else\n");
	fprintf(fd2, "    echo set to USB device 0 and use memory area start from 0x%x\n", ADDRESS_FATLOAD);
	fprintf(fd2, "    setenv isp_if usb\n");
	fprintf(fd2, "    setenv isp_dev 0\n");
	fprintf(fd2, "    setenv isp_ram_addr 0x%x\n", ADDRESS_FATLOAD);
	fprintf(fd2, "    $isp_if start\n");
	fprintf(fd2, "fi\n\n");
	fprintf(fd2, "fatinfo $isp_if $isp_dev\n");
	fprintf(fd2, "fatls   $isp_if $isp_dev /\n\n");

	for (i == 0; i < NUM_OF_PARTITION; i++) {
		if (file_header_extract4boot2linux.partition_info[i].file_size == 0) {
			break;
		}

		fprintf(fd2, "echo Loading %s to $addr_dst_%s\n", partition_to_be_loaded[i], partition_to_be_loaded[i]);
		if(extrac4boot2linux_src == EXTRACT4BOOT2LINUX_FOR_SDCARD && strcmp(file_header_extract4boot2linux.partition_info[i].file_name,"kernel")==0)
		{
			
			fprintf(fd2, "fatsize $isp_if $isp_dev /uImage \n\n");
			fprintf(fd2, "fatload $isp_if $isp_dev $addr_dst_%s /uImage $filesize 0\n\n",partition_to_be_loaded[i]);
		}
		else if(extrac4boot2linux_src == EXTRACT4BOOT2LINUX_FOR_SDCARD && strcmp(file_header_extract4boot2linux.partition_info[i].file_name,"dtb")==0)
		{
			fprintf(fd2, "fatsize $isp_if $isp_dev /dtb \n\n");
			fprintf(fd2, "fatload $isp_if $isp_dev $addr_dst_%s /dtb $filesize 0\n\n",partition_to_be_loaded[i]);
		}
		else
		{
		fprintf(fd2, "fatload $isp_if $isp_dev $addr_dst_%s /ISPBOOOT.BIN 0x%x 0x%x\n\n",
			partition_to_be_loaded[i],
			file_header_extract4boot2linux.partition_info[i].file_size,
			file_header_extract4boot2linux.partition_info[i].file_offset);
		}
	}

	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (file_header_extract4boot2linux.partition_info[i].file_size == 0) {
			break;
		}
		if(extrac4boot2linux_src == EXTRACT4BOOT2LINUX_FOR_SDCARD && (strcmp(file_header_extract4boot2linux.partition_info[i].file_name,"kernel")==0 ||\
			strcmp(file_header_extract4boot2linux.partition_info[i].file_name,"dtb")==0))
		{
			fprintf(fd2,"echo sdcard %s no need do md5 check!!!\n",file_header_extract4boot2linux.partition_info[i].file_name);
			continue;
		}
		fprintf(fd2, "echo verifying %s\n", partition_to_be_loaded[i]);
		fprintf(fd2, "md5sum $addr_dst_%s 0x%x md5sum_value\n",
			partition_to_be_loaded[i],
			file_header_extract4boot2linux.partition_info[i].file_size);
		fprintf(fd2, "if test \"$md5sum_value\" = %s ; then\n", file_header_extract4boot2linux.partition_info[i].md5sum);
		fprintf(fd2, "    echo md5sum: OK.\n");
		fprintf(fd2, "else\n");
		fprintf(fd2, "    echo md5sum: Error!\n");
		fprintf(fd2, "    exit -1\n");
		fprintf(fd2, "fi\n\n");
	}

	if (file_header_extract4boot2linux.partition_info[2].file_size == 0) {
		fprintf(fd2, "bootm ${addr_dst_%s} - ${addr_dst_%s}\n",
			partition_to_be_loaded[0],
			partition_to_be_loaded[1]);
	} else {
		fprintf(fd2, "bootm ${addr_dst_%s} ${addr_dst_%s}  ${addr_dst_%s}\n",
			partition_to_be_loaded[0],
			partition_to_be_loaded[2],
			partition_to_be_loaded[1]);
	}

	fclose(fd2);

#if !defined(MESSAGE_OUT_NONE)
	printf("\nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
	sprintf(cmd, "cat %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);
	printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");
#endif

	/* file_header.init_script[], there is no main script */
	sprintf(tmp_file_init_script, "tmp_file_init_script%08x", (u32)(getpid()));
	sprintf(cmd, "mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n \"Init script\" -d %s %s %s", tmp_file, tmp_file_init_script, MESSAGE_OUT);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "rm -f %s", tmp_file);
	// printf("%s\n", cmd);
	system(cmd);

	if (stat(tmp_file_init_script, &file_stat) == 0) {
		if (file_stat.st_size > SIZE_INIT_SCRIPT) {
			printf("\n\n\nSIZE_INIT_SCRIPT is too small\n\n");
			exit(-1);
		}

		fd2 = fopen(tmp_file_init_script, "rb");
		if (fread(file_header_extract4boot2linux.init_script, file_stat.st_size, 1, fd2) != 1) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}
		fclose(fd2);
	}

	sprintf(cmd, "rm -f %s", tmp_file_init_script);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(tmp_file_file_header, "tmp_file_file_header%08x", (u32)(getpid()));
	fd2 = fopen(tmp_file_file_header, "wb");
	if (fd2 == NULL) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
	if (fwrite(&file_header_extract4boot2linux, sizeof(file_header_extract4boot2linux), 1, fd2) != 1) {
		printf("Error: %s: %d\n", __FILE__, __LINE__);
		exit(-1);
	}
	fclose(fd2);

	/* concatenate files */
	sprintf(cmd, "dd if=%s of=%s bs=1024 skip=0 count=%u %s",
		argv[ARGC_EXTRACT4BOOT2LINUX_INPUT], argv[ARGC_EXTRACT4BOOT2LINUX_OUTPUT],
		((FILE_SIZE_IMAGE_XBOOT0 + FILE_SIZE_IMAGE_UBOOT0) >> 10), MESSAGE_OUT);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "cat %s >> %s", tmp_file_file_header, argv[ARGC_EXTRACT4BOOT2LINUX_OUTPUT]);
	// printf("%s\n", cmd);
	system(cmd);

	sprintf(cmd, "rm -f %s", tmp_file_file_header);
	// printf("%s\n", cmd);
	system(cmd);

	for (i = 0; i < NUM_OF_PARTITION; i++) {
		if (file_header_extract4boot2linux.partition_info[i].file_size == 0)
			continue;

		idx = get_partition_info_idx_by_file_name(file_header_extract4boot2linux.partition_info[i].file_name);
		if (idx < 0) {
			printf("Error: %s: %d\n", __FILE__, __LINE__);
			exit(-1);
		}
		sprintf(tmp_file, "tmp%08x", (u32)(getpid()));
		sprintf(cmd, "dd if=%s of=%s bs=1024 skip=%u count=%u %s", argv[ARGC_EXTRACT4BOOT2LINUX_INPUT], tmp_file,
			(isp_info.file_header.partition_info[idx].file_offset >> 10),
			(file_header_extract4boot2linux.partition_info[i].file_size >> 10), MESSAGE_OUT);
		// printf("%s\n", cmd);
		system(cmd);

		sprintf(cmd, "cat %s >> %s", tmp_file, argv[ARGC_EXTRACT4BOOT2LINUX_OUTPUT]);
		// printf("%s\n", cmd);
		system(cmd);

		sprintf(cmd, "rm -f %s", tmp_file);
		// printf("%s\n", cmd);
		system(cmd);
	}

	return 0;
}

int main(int argc, char **argv)
{
	char *sub_cmd;
	int i;

	if (argc < 2) {
		printf("\n\nUsage:\n");
		printf("    %s sub_cmd ... \n\n", argv[0]);
		return -1;
	} else
		sub_cmd = argv[1];

	/*
	printf("--------------------------------\n");
	for(i=0; i<argc; i++) {
		printf("%s \n", argv[i]);
	}
	printf("--------------------------------\n");
	*/

	// Initialize global data.
	memset(&isp_info, 0, sizeof(isp_info));
	strncpy(isp_info.file_header.signature, file_header_signature, sizeof(isp_info.file_header.signature));

	if (strcmp(sub_cmd, "pack_image") == 0) {
		return pack_image(argc, argv);
	} else if (strcmp(sub_cmd, "extract4boot2linux_sdcardboot") == 0) {
		return extract4boot2linux(argc, argv,EXTRACT4BOOT2LINUX_FOR_SDCARD);
	} else if (strcmp(sub_cmd, "extract4boot2linux") == 0) {
		return extract4boot2linux(argc, argv,EXTRACT4BOOT2LINUX_FOR_OTHER);
	} else if (strcmp(sub_cmd, "extract4update") == 0) {
		return extract4update(argc, argv, EXTRACT4UPDATE_FROM_STORAGE);
	} else if (strcmp(sub_cmd, "extract4tftpupdate") == 0) {
		return extract4update(argc, argv, EXTRACT4UPDATE_FROM_TFTP);
	}  else {
		printf("Unknown sub_cmd: %s\n\n", sub_cmd);
		return 1;
	}

	return 0;
}
